package com.edocbase.desktop.lightweight.widgets.richeditor;

import static com.edocbase.desktop.lightweight.widgets.richeditor.IHTMLDocumentConstants.DesignModeProperty;
import static com.edocbase.desktop.lightweight.widgets.richeditor.IHTMLDocumentConstants.OFF;
import static com.edocbase.desktop.lightweight.widgets.richeditor.IHTMLDocumentConstants.ON;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.swt.SWT;
import org.eclipse.swt.browser.Browser;
import org.eclipse.swt.browser.ProgressEvent;
import org.eclipse.swt.browser.ProgressListener;
import org.eclipse.swt.internal.ole.win32.COM;
import org.eclipse.swt.ole.win32.OleAutomation;
import org.eclipse.swt.ole.win32.OleControlSite;
import org.eclipse.swt.ole.win32.Variant;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;

import com.edocbase.desktop.lightweight.widgets.richeditor.commands.DocumentCommand;
import com.edocbase.desktop.lightweight.widgets.richeditor.listener.NodeSelectionChangeListener;
import com.edocbase.desktop.lightweight.widgets.richeditor.listener.NodeSelectionEvent;

public class RichEditor {
	protected final Browser browser;
	OleAutomation auto;
	OleControlSite site;
	private boolean complated = false;
	private final Map<String, DocumentCommand> trackedCommands = new HashMap<String, DocumentCommand>();
	private transient List<NodeSelectionChangeListener> selectionListenerList = new ArrayList<NodeSelectionChangeListener>();
	private final List<DocumentCommand> pendingCommands = Collections
			.synchronizedList(new ArrayList<DocumentCommand>());
	private boolean initEditable = false;
	public RichEditor(Composite parent, int style) {
		browser = new Browser(parent, style);
		try {
			Field field = browser.getClass().getDeclaredField("webBrowser");
			field.setAccessible(true);
			Object obj = field.get(browser);
			Field autoField = obj.getClass().getDeclaredField("auto");
			autoField.setAccessible(true);
			Object autoObj = autoField.get(obj);
			if (autoObj != null && autoObj instanceof OleAutomation) {
				auto = (OleAutomation) autoObj;
			}

			Field siteField = obj.getClass().getDeclaredField("site");
			siteField.setAccessible(true);
			Object siteObj = siteField.get(obj);
			if (siteObj != null && siteObj instanceof OleControlSite) {
				site = (OleControlSite) siteObj;
			}
			browser.addProgressListener(new ProgressListener() {

				@Override
				public void completed(ProgressEvent event) {
					complated = true;
					initialize();
				}

				@Override
				public void changed(ProgressEvent event) {

				}
			});
			Listener documentListener = new Listener() {
				@Override
				public void handleEvent(Event event) {
					handleDomEvent(event);
				}

			};
			browser.addListener(SWT.MouseUp, documentListener);
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchFieldException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
	}

	public Browser getBrowser() {
		return browser;
	}

	public void setEditable(boolean editable) {
		if (complated) {
			String mode = editable ? ON : OFF;
			HTMLDocument2 doc = getHTMLDocument();
			if (doc != null) {
				try {
					doc.setProperty(DesignModeProperty, mode);
				} finally {
					doc.dispose();
				}
			}
		}else{
			initEditable =true;
		}

	}

	public boolean getEditable() {
		HTMLDocument2 doc = getHTMLDocument();
		if (doc != null) {
			try {
				String value = doc.getProperty(DesignModeProperty);
				// System.out.println("editable... " + value);
				return value.equals(ON);
			} finally {
				doc.dispose();
			}
		}
		return false;
	}

	public String getHtml() {
		return browser.getText();
	}

	public String getPlainText() {
		HTMLDocument2 doc = getHTMLDocument();
		if (doc != null) {
			DocumentElement docElement = doc.getDocumentElement();
			try {
				if (docElement != null) {
					return docElement.getOuterText();
				}
			} finally {
				doc.dispose();
				if (docElement != null) {
					docElement.dispose();
				}
			}
		}
		return null;
	}

	void handleDomEvent(Event event) {
		HTMLDocument2 doc = getHTMLDocument();
		if (doc != null) {
			try {
				switch (event.type) {
				case SWT.MouseUp:
					NodeSelectionEvent nodeSelectionEvent = new NodeSelectionEvent(
							null);
					fireNodeSelection(nodeSelectionEvent);
					break;
				}
			} finally {
				doc.dispose();
			}
		}
	}

	void fireNodeSelection(NodeSelectionEvent nodeEvent) {
		if (selectionListenerList.size() > 0) {
			for (NodeSelectionChangeListener listener : selectionListenerList) {
				listener.selectedNodeChanged(nodeEvent);
			}
		}
	}

	public HTMLDocument2 getHTMLDocument() {
		/* get IHTMLDocument2 */
		int[] rgdispid = auto.getIDsOfNames(new String[] { "Document" }); //$NON-NLS-1$
		int dispIdMember = rgdispid[0];
		Variant pVarResult = auto.getProperty(dispIdMember);
		if (pVarResult == null || pVarResult.getType() == COM.VT_EMPTY) {
			if (pVarResult != null)
				pVarResult.dispose();
			return null;
		}
		OleAutomation document = pVarResult.getAutomation();
		pVarResult.dispose();
		return new HTMLDocument2(document);
	}

	public void open(String url) {
		browser.setUrl(url);
	}

	public void setFocus() {
		browser.setFocus();
	}

	public void execute(DocumentCommand command) {
		if (complated) {
			HTMLDocument2 doc = getHTMLDocument();
			if (doc != null) {
				try {
					String cmdId = command.getCommandIdentifier();
					boolean showUI = command.showUI();
					Object para = command.getValue();
					doc.execCommand(cmdId, showUI, para);
				} finally {
					doc.dispose();
				}
			}
		} else {
			pendingCommands.add(command);
		}
	}

	void initialize() {
		for (DocumentCommand command : pendingCommands) {
			execute(command);
		}
		pendingCommands.clear();
		if (initEditable){
			setEditable(true);
		}
	}

	public void addNodeSelectionChangeListener(
			NodeSelectionChangeListener listener) {
		selectionListenerList.add(listener);
	}

	public void removeNodeSelectionChangeListener(
			NodeSelectionChangeListener listener) {
		selectionListenerList.remove(listener);
	}

	public void trackCommand(DocumentCommand command) {
		trackedCommands.put(command.getCommandIdentifier(), command);
	}

	public void untrackCommand(DocumentCommand command) {
		trackedCommands.remove(command.getCommandIdentifier());
	}

	public void dispose() {
		browser.dispose();
		site.dispose();
		auto.dispose();
		Collection<DocumentCommand> values = trackedCommands.values();
		for (DocumentCommand command : values) {
			command.dispose();
		}
	}

	public void setHtml(String script) {
		browser.setText(script);
	}

}
